package cn.lnl.springcloud.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁
 * @author 李年林
 * @since 2021/8/26 17:07
 */
public class RedisLock {

    private RedisTemplate redisTemplate;
    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 键--前缀
     */
    private static final String LOCK_KEY = "Redis:Lock:";

    /**
     * 锁释放成功返回值
     */
    private static final Long UNLOCK_SUCCESS = 1L;

    /**
     * 最大等待时间
     */
    private static final Long MAX_WAIT_TIME = 60000L;

    private static final Long LOOP_GET_LOCK_TIME = 50L;

    /**
     * 释放锁脚本
     */
    private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end ";

    /**
     * 默认键过期时间
     */
    public static final Long EXPIRE_TIME = 10L;

    /**
     * 默认等待时间
     */
    public static final Long WAIT_TIME = -1L;

    /**
     * 键
     */
    private String key;

    /**
     * 值
     */
    private String value;

    /**
     * 过期时间
     */
    private Long expireTime;

    /**
     * 等待时间
     */
    private Long waitTime;


    /**
     * 数据初始化,无参
     */
    public RedisLock() {
        this.expireTime = EXPIRE_TIME;
        this.waitTime = WAIT_TIME;
    }

    /**
     * 存放键
     * @param key
     */
    public RedisLock(String key) {
        this.expireTime = EXPIRE_TIME;
        this.waitTime = WAIT_TIME;
        this.key = LOCK_KEY+key;
        this.value = this.value();
    }

    /**
     * 生成值
     * @return
     */
    private String value() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static RedisLock createLock (String key) {
        return new RedisLock(key);
    }


    /**
     * 上锁
     * @return
     */
    public Boolean lock() {
        //如果当前key不存在，那么将设置
        if (redisTemplate.opsForValue().setIfAbsent(this.key, this.value, this.expireTime, TimeUnit.MILLISECONDS)) {
            return Boolean.TRUE;
        } else if (this.waitTime < 0L) {
            return Boolean.FALSE;
        } else {
            try {
                //休眠获取锁的时间
                Thread.sleep(LOOP_GET_LOCK_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.waitTime = this.waitTime - LOOP_GET_LOCK_TIME;
            return this.lock();
        }
    }

    public Boolean unLock() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end ");
        redisScript.setResultType(Long.class);
        return UNLOCK_SUCCESS.equals(redisTemplate.execute(redisScript, Collections.singletonList(this.key), new Object[]{this.value}));
    }






















}
